
<template>
    <div>
        <h3>WebSocket状态: {{ connectionStatus }}</h3>
        <button @click="initWebSocket" :disabled="isConnected">连接</button>
        <button @click="closeWebSocket" :disabled="!isConnected">断开</button>
        <div>
            <input v-model="message" placeholder="输入消息" />
            <button @click="sendMessage" :disabled="!isConnected">发送</button>
        </div>
        <div class="message-box">
            <h4>接收消息:</h4>
            <div v-for="(msg, index) in receivedMessages" :key="index">{{ msg }}</div>
        </div>
    </div>
</template>
  
<script setup>
import { ref, onUnmounted } from 'vue';

const wsUrl = 'wss://echo.websocket.org';
const socket = ref(null);
const isConnected = ref(false);
const message = ref('');
const receivedMessages = ref([]);
const connectionStatus = ref('未连接');
const reconnectAttempts = ref(0);
const maxReconnectAttempts = 5;
const heartbeatInterval = ref(null);
const heartbeatTime = 30000; // 30秒心跳间隔(推荐25s-40s)

const initWebSocket = () => {
    socket.value = new WebSocket(wsUrl);

    socket.value.onopen = () => {
        isConnected.value = true;
        connectionStatus.value = '已连接';
        reconnectAttempts.value = 0;
        startHeartbeat();
        console.log('WebSocket连接成功');
    };

    socket.value.onmessage = (event) => {
        if (event.data !== 'pong') { // 过滤心跳响应
            receivedMessages.value.push(event.data);
        }
        resetHeartbeat(); // 收到消息重置心跳计时
    };

    socket.value.onerror = (error) => {
        console.error('WebSocket错误:', error);
    };

    socket.value.onclose = () => {
        isConnected.value = false;
        connectionStatus.value = '已断开';
        stopHeartbeat();
        handleReconnect();
    };
};

const startHeartbeat = () => {
    heartbeatInterval.value = setInterval(() => {
        if (socket.value?.readyState === WebSocket.OPEN) {
            socket.value.send('ping');
            console.log('发送心跳ping');
        }
    }, heartbeatTime);
};

const resetHeartbeat = () => {
    if (heartbeatInterval.value) {
        clearInterval(heartbeatInterval.value);
        startHeartbeat();
    }
};

const stopHeartbeat = () => {
    if (heartbeatInterval.value) {
        clearInterval(heartbeatInterval.value);
        heartbeatInterval.value = null;
    }
};

const handleReconnect = () => {
    if (reconnectAttempts.value < maxReconnectAttempts) {
        const delay = Math.min(1000 * Math.pow(2, reconnectAttempts.value), 30000);
        reconnectAttempts.value++;
        connectionStatus.value = `尝试重连(${reconnectAttempts.value}/${maxReconnectAttempts})`;

        setTimeout(() => {
            console.log(`第${reconnectAttempts.value}次重连...`);
            initWebSocket();
        }, delay);
    } else {
        connectionStatus.value = '重连失败';
    }
};

const sendMessage = () => {
    if (socket.value?.readyState === WebSocket.OPEN) {
        socket.value.send(message.value);
        message.value = '';
    }
};

const closeWebSocket = () => {
    if (socket.value) {
        socket.value.close();
    }
};

onUnmounted(() => {
    closeWebSocket();
    stopHeartbeat();
});

// 初始化连接
initWebSocket();
</script>
  
<style scoped>
.message-box {
    margin-top: 20px;
    border: 1px solid #eee;
    padding: 10px;
    max-height: 300px;
    overflow-y: auto;
}
</style>
  